using System;
using System.Collections;
using Castle.MonoRail.TestSupport;
using NHibernate;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Constraints;
using DecisionesInteligentes.iEvalua.Web.Controllers;
using DecisionesInteligentes.iEvalua.Web.Services;
using DecisionesInteligentes.iEvalua.Models;
using DecisionesInteligentes.iEvalua.Helpers;
using DecisionesInteligentes.iEvalua.Tests.Helpers;

namespace DecisionesInteligentes.iEvalua.Tests.Controllers {
	[TestFixture]
	public class UsersControllerTestCase : ControllerARMockTestCase<UsersController, User>
	{
		protected override void SetupApplication()
		{
			using(new MockScope(session))
			{
				Application application = controller.Application;
				
				Assert.IsNotNull(application);
				Assert.AreEqual(application.Id, 1);
			}
		}
		
		public override void SetUp()
		{
			base.SetUp();
			
			Password password = new Password();
			ISecurityService securityService = new SecurityService(password);
			controller = new UsersController(securityService);
			
			DoApplicationSetup();
		}
		
		[Test]
		public void Show_Users_List()
		{
			IList users = new ArrayList {
				ModelMocks.GetUser()
			};
			
			ICriteria criteria = mocks.CreateMock<ICriteria>();

			using(mocks.Unordered())
			{
				Expect.Call(session.CreateCriteria(typeof(User))).Return(criteria);
				Expect.Call(criteria.Add(null)).Constraints(
					Property.Value("Value", 1) & Property.Value("PropertyName", "ApplicationId")
				).Return(criteria);
				Expect.Call(criteria.List()).Return(users);
			}
			
			mocks.ReplayAll();

			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "Index");
				controller.Index();
		
				Assert.IsNotNull(controller.PropertyBag["entities"]);
			}
		}
		
		[Test]
		public void Create_New_User()
		{
			using(mocks.Unordered())
			{
				Expect.Call(session.Save(null)).IgnoreArguments().Return(null);
			}
			
			mocks.ReplayAll();

			using(new MockScope(session))
			{
				User user = ModelMocks.GetUser();
				user.Application = null;
				PrepareController(controller, "", "Users", "Create");
				controller.Create(user);
		
				Assert.AreEqual(controller.Flash["message"], "A new User has been created");
				Assert.AreEqual("/Users/Edit.rails?id=1&", Response.RedirectedTo);
			}
		}
		
		[Test]
		public void Create_Invalid_New_User()
		{
			mocks = null;
			
			User user = ModelMocks.GetUser();
			user.Username = null;
			
			PrepareController(controller, "", "Users", "Create");
			controller.Create(user);
	
			Assert.AreEqual("User has some invalid data", controller.Flash["message"]);
			Assert.IsNotNull(controller.PropertyBag["entity"]);
			Assert.IsNotNull(controller.Flash["error"]);
			Assert.AreEqual("Username cannot be empty", ((string[])controller.Flash["error"])[0]);
			Assert.AreEqual("/Users/New.rails", Response.RedirectedTo);
		}
		
		[Test]
		public void Create_Fail_New_User()
		{
			using(mocks.Unordered())
			{
				Expect.Call(session.Save(null))
					.IgnoreArguments()
					.Throw(new Exception("Failed to save this user."));
			}
			
			mocks.ReplayAll();

			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "Create");
				controller.Create(ModelMocks.GetUser());
				
				Assert.AreEqual("User was not saved due some errors.", controller.Flash["message"]);
				Assert.IsNotNull(controller.PropertyBag["entity"]);
				Assert.AreEqual("Could not perform Create for User", ((string[])controller.Flash["error"])[0]);
				Assert.AreEqual("/Users/New.rails", Response.RedirectedTo);
			}
		}
		
		[Test]
		public void ViewEdit_Existing_User()
		{
			using(mocks.Unordered())
			{
				Expect.Call(session.Load(typeof(User), 1)).Return(ModelMocks.GetUser());
			}
			
			mocks.ReplayAll();
			
			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "View");
				controller.View(1);
				
				Assert.IsNull(controller.Flash["message"]);
				Assert.IsNotNull(controller.PropertyBag["entity"]);
				Assert.IsNull(controller.Flash["error"]);
			}
		}
		
		[Test]
		public void ViewEdit_Non_Existing_User()
		{
			using(mocks.Unordered())
			{
				Expect.Call(session.Load(typeof(User), 0)).Return(null);
			}
			
			mocks.ReplayAll();
			
			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "View");
				controller.View(0);
				
				Assert.AreEqual("Failed to load User", controller.Flash["message"]);
				Assert.IsNull(controller.PropertyBag["entity"]);
				Assert.AreEqual("User not found", ((string[])controller.Flash["error"])[0]);
				Assert.AreEqual("/Users/Index.rails", Response.RedirectedTo);
			}
		}
		
		[Test]
		public void ViewEdit_Load_Fail_Exception()
		{
			using(mocks.Unordered())
			{
				Expect.Call(session.Load(typeof(User), 1))
					.IgnoreArguments()
					.Throw(new Exception("Unknown exception"));
			}
			
			mocks.ReplayAll();
			
			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "View");
				controller.View(1);
				
				Assert.AreEqual("Failed to load User", controller.Flash["message"]);
				Assert.IsNull(controller.PropertyBag["entity"]);
				Assert.AreEqual("Could not perform FindByPrimaryKey for User. Id: 1", 
					((string[])controller.Flash["error"])[0]);
				Assert.AreEqual("/Users/Index.rails", Response.RedirectedTo);
			}
		}
		
		[Test]
		public void Save_Existing_User()
		{
			session.Update(null);
			LastCall.IgnoreArguments();
		
			mocks.ReplayAll();
		
			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "Save");
				controller.Save(ModelMocks.GetUser());
		
				Assert.AreEqual("User has been saved", controller.Flash["message"]);
				Assert.IsNull(controller.PropertyBag["entity"]);
				Assert.IsNull(controller.Flash["error"]);
				Assert.AreEqual("/Users/Edit.rails?id=1&", Response.RedirectedTo);
			}
		}
		
		[Test]
		public void Save_Invalid_User()
		{
			mocks = null;
		
			User user = ModelMocks.GetUser();
			user.Username = null;
			
			PrepareController(controller, "", "Users", "Save");
			controller.Save(user);
	
			Assert.AreEqual("User has some invalid data", controller.Flash["message"]);
			Assert.IsNotNull(controller.PropertyBag["entity"]);
			Assert.AreEqual("Username cannot be empty", ((string[])controller.Flash["error"])[0]);
			Assert.AreEqual("/Users/Edit.rails", Response.RedirectedTo);
		}
		
		[Test]
		public void Save_Exception()
		{
			using(mocks.Unordered())
			{
				session.Update(null);
				LastCall.IgnoreArguments()
					.Throw(new Exception("Unknown exception"));
			}
			
			mocks.ReplayAll();
			
			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "Save");
				controller.Save(ModelMocks.GetUser());
				
				Assert.AreEqual("User was not saved due some errors.", controller.Flash["message"]);
				Assert.IsNotNull(controller.PropertyBag["entity"]);
				Assert.AreEqual("Could not perform Update for User", 
					((string[])controller.Flash["error"])[0]);
				Assert.AreEqual("/Users/Edit.rails", Response.RedirectedTo);
			}
		}
		
		[Test]
		public void Change_User_Password()
		{
			using(mocks.Unordered())
			{
				Expect.Call(session.Load(typeof(User), 1)).Return(ModelMocks.GetUser());
				
				session.Update(null);
				LastCall.IgnoreArguments();
			}
			
			mocks.ReplayAll();
			
			using(new MockScope(session))
			{
				PrepareController(controller, "", "Users", "SaveChangePassword");
				controller.SaveChangePassword(1, "mynewpass", "mynewpass");
				
				Assert.AreEqual("Password has been changed", controller.Flash["message"]);
				Assert.IsNull(controller.PropertyBag["entity"]);
				Assert.IsNull(controller.Flash["error"]);
				Assert.AreEqual("/Users/Edit.rails?id=1&", Response.RedirectedTo);
			}
		}
		
		
		[Test]
		public void HashPassword_Test()
		{
			mocks = null;
			
			string validPassword = "mypass";
			string invalidPassword = "mmypass";
			
			Password pwd = new Password();
			byte[] salt = pwd.CreateRandomSalt();
			
			string saltedhashedPassword = pwd.ComputeSaltedHash(validPassword, salt);
			
			//Console.WriteLine(saltedhashedPassword);
			
			Assert.IsTrue(pwd.VerifyPassword(validPassword, saltedhashedPassword));
			Assert.IsFalse(pwd.VerifyPassword(invalidPassword, saltedhashedPassword));
		}
	}
}
